Imports System.ComponentModel
Imports WRDB.Project
Imports DotSpatial.Controls

''' <summary>
''' Base class to store information for each layer in map
''' </summary>
''' <remarks>Note that all information is stored in the map layer object (not stored locally)</remarks>
Public MustInherit Class clsLayer
    Public Layer As DotSpatial.Symbology.FeatureLayer
    Friend Map As DotSpatial.Controls.Map

    Friend Sub New(ByVal GisMap As DotSpatial.Controls.Map, ByVal Layer As DotSpatial.Symbology.FeatureLayer)
        Map = GisMap
        Me.Layer = Layer
    End Sub

    Friend ReadOnly Property LayerType() As DotSpatial.Topology.FeatureType
        Get
            Return LayerType(Layer)
        End Get
    End Property

    Friend Shared ReadOnly Property LayerType(Layer As DotSpatial.Symbology.FeatureLayer) As DotSpatial.Topology.FeatureType
        Get
            Return Layer.DataSet.FeatureType
        End Get
    End Property

    ''' <summary>
    ''' Convenience function to return shapefile object for this layer
    ''' </summary>
    Friend ReadOnly Property FeatureSet() As DotSpatial.Data.FeatureSet
        Get
            Return Layer.DataSet
        End Get
    End Property

    ''' <summary>
    ''' Shared function to create new map layer using specified shapefile
    ''' </summary>
    Friend Shared Function Add(ByVal Map As DotSpatial.Controls.Map, ByVal FileName As String) As clsLayer
        Try
            If Not My.Computer.FileSystem.FileExists(FileName) Then Return Nothing
            With Map
                Dim fs As New DotSpatial.Data.FeatureSet
                fs.Open(FileName)
                If fs IsNot Nothing Then
                    If Not fs.Projection.IsValid Then
                        frmInfoMsg.ShowInfoMsg("Projection Missing", "This shapefile ({0}) has no projection associated with it. The layer will be assumed to use latitude/longitude coordinates, which may cause objects to appear in the wrong location.\n\nWe recommend that you use the tools menu to assign or convert projections to all shapefiles you intend to view with WRDB.", IO.Path.GetFileName(FileName))
                        fs.Projection = DotSpatial.Projections.KnownCoordinateSystems.Geographic.World.WGS1984
                    End If

                    'assign first projected shapefile as project projection
                    If (ProjectProjection.IsLatLon Or .Layers.Count = 0) AndAlso fs.Projection.IsValid AndAlso Not fs.Projection.IsLatLon Then
                        ProjectProjection = fs.Projection
                    End If

                    'later ones must compare; if WRDB Stations layer already has been added, reproject it without comment
                    Dim IsStationsLayer As Boolean = IO.Path.GetFileNameWithoutExtension(FileName).Equals(WRDBFileName, StringComparison.OrdinalIgnoreCase)

                    If ProjectProjection IsNot Nothing AndAlso Not fs.Projection.Equals(ProjectProjection) Then
                        If Not IsStationsLayer Then frmInfoMsg.ShowInfoMsg("Projection Mismatch", "This shapefile ({2}) is using a different projection ({0}) than others in this map ({1}). The shape file will be reprojected in memory so it can be viewed properly (the original file will not be altered). To avoid this message in the future, we recommend that you use the Tools menu to assign or convert projections to all shapefiles you intend to view with WRDB.", fs.Projection.ToString, ProjectProjection.ToString, IO.Path.GetFileName(FileName))
                        fs.Reproject(ProjectProjection)
                    Else
                        fs.Projection = ProjectProjection
                    End If

                    'find existing WRDB Stations layer and make sure it uses this projection (will not apply if adding WRDB Stations at this time, because is not a layer yet)
                    For i As Integer = 0 To .Layers.Count - 1
                        If String.Equals(.Layers(i).LegendText, WRDBLayerName, StringComparison.OrdinalIgnoreCase) AndAlso Not .Layers(i).Projection.Equals(ProjectProjection) Then
                            .Layers(i).Reproject(ProjectProjection)
                        End If
                    Next
                End If

                Dim lyr As clsLayer = Nothing
                Dim fsNew As New DotSpatial.Data.FeatureSet
                fsNew.Open(FileName)
                Select Case fs.FeatureType
                    Case DotSpatial.Topology.FeatureType.Point
                        lyr = New clsLayerPoint(Map, Map.Layers.Add(fs))
                    Case DotSpatial.Topology.FeatureType.Polygon
                        lyr = New clsLayerPolygon(Map, Map.Layers.Add(fs))
                    Case DotSpatial.Topology.FeatureType.Line
                        lyr = New clsLayerPolyline(Map, Map.Layers.Add(fs))
                End Select
                lyr.SetName(Strings.StrConv(IO.Path.GetFileNameWithoutExtension(FileName).Replace("-reproject", ""), VbStrConv.ProperCase))

                'find max color used
                Dim maxcolor As Integer = -1
                For i As Integer = 0 To Map.Layers.Count - 2
                    Dim clr As Color
                    Select Case LayerType(Map.Layers(i))
                        Case DotSpatial.Topology.FeatureType.Line
                            clr = CType(Map.Layers(i), DotSpatial.Controls.MapLineLayer).Symbolizer.GetFillColor
                        Case DotSpatial.Topology.FeatureType.Polygon
                            clr = CType(Map.Layers(i), DotSpatial.Controls.MapPolygonLayer).Symbolizer.GetFillColor
                        Case DotSpatial.Topology.FeatureType.Point
                            clr = CType(Map.Layers(i), DotSpatial.Controls.MapPointLayer).Symbolizer.GetFillColor
                    End Select
                    For j As Integer = 0 To DefaultLayerColor.Length - 1
                        If DefaultLayerColor(j).ToArgb = clr.ToArgb Then
                            maxcolor = Math.Max(maxcolor, j)
                            Exit For
                        End If
                    Next
                Next
                If TypeOf lyr Is clsLayerPoint Then
                    With CType(lyr, clsLayerPoint)
                        .FillColor = DefaultLayerColor(maxcolor + 1)
                        .PointSize = 12
                        .PointShape = DotSpatial.Symbology.PointShape.Ellipse
                        .OutlineColor = Color.Black
                    End With
                ElseIf TypeOf lyr Is clsLayerPolygon Then
                    With CType(lyr, clsLayerPolygon)
                        '.Filled = True
                        .FillColor = DefaultLayerColor(maxcolor + 1)
                        .LineColor = Color.Black
                        '.FillStipple = Drawing2D.DashStyle.Solid
                    End With
                Else
                    With CType(lyr, clsLayerPolyline)
                        .LineColor = DefaultLayerColor(maxcolor + 1)
                        .LineWidth = 1
                    End With
                End If
                Return lyr
            End With
        Catch ex As Exception
            ErrorMsg(, ex)
            Return Nothing
        End Try
    End Function

    ''' <summary>
    ''' Create and return new layer object based on position in the layer list
    ''' </summary>
    Public Shared Function Reset(ByVal Map As DotSpatial.Controls.Map, ByVal LayerPosition As Integer) As clsLayer
        Try
            Dim lyr As clsLayer = Nothing
            Select Case LayerType(Map.Layers(LayerPosition))
                Case DotSpatial.Topology.FeatureType.Point
                    lyr = New clsLayerPoint(Map, Map.Layers(LayerPosition))
                Case DotSpatial.Topology.FeatureType.Line
                    lyr = New clsLayerPolyline(Map, Map.Layers(LayerPosition))
                Case DotSpatial.Topology.FeatureType.Polygon
                    lyr = New clsLayerPolygon(Map, Map.Layers(LayerPosition))
            End Select
            Return lyr
        Catch ex As Exception
            ErrorMsg(, ex)
            Return Nothing
        End Try
    End Function

    ''' <summary>
    ''' Create and return new layer object based on layer name
    ''' </summary>
    Public Shared Function Reset(ByVal Map As DotSpatial.Controls.Map, ByVal LayerName As String) As clsLayer
        For i As Integer = 0 To Map.Layers.Count - 1
            If Map.Layers(i).LegendText.ToUpper = LayerName.ToUpper Then
                Return Reset(Map, i)
            End If
        Next
        Return Nothing
    End Function

    ''' <summary>
    ''' Helper routine to move current polygon layer below all non-polygon layers (so filled polygons don't obstruct)
    ''' </summary>
    Friend Sub MoveBelowPolylineLayers()
        Try
            If LayerType(Layer) <> DotSpatial.Topology.FeatureType.Polygon Then Exit Sub
            With Map
                For i As Integer = 0 To .Layers.IndexOf(Layer) - 1
                    If LayerType(.Layers(i)) <> DotSpatial.Topology.FeatureType.Polygon Then
                        Layer.LockDispose()
                        Dim LegendText As String = Layer.LegendText
                        .Layers.Remove(Layer)
                        .Layers.Insert(i, Layer)
                        Layer.LegendText = LegendText
                        Layer.UnlockDispose()
                        Exit For
                    End If
                Next
            End With
        Catch ex As Exception
            ErrorMsg(, ex)
        End Try
    End Sub

    ''' <summary>
    ''' Move current layer down in list
    ''' </summary>
    Friend Sub MoveUp()
        With Map
            .Layers.SuspendEvents()
            Dim i As Integer = .Layers.IndexOf(Layer)
            If i < .Layers.Count - 1 Then
                Layer.LockDispose()
                Dim LegendText As String = Layer.LegendText
                .Layers.Remove(Layer)
                .Layers.Insert(i + 1, Layer)
                Layer.LegendText = LegendText
                Layer.UnlockDispose()
            End If
            .Layers.ResumeEvents()
        End With
    End Sub

    Friend Shared Sub MoveTop(Map As DotSpatial.Controls.Map, Layer As DotSpatial.Symbology.FeatureLayer)
        With Map
            .Layers.SuspendEvents()
            Dim LegendText As String = Layer.LegendText
            Layer.LockDispose()
            .Layers.Remove(Layer)
            .Layers.Insert(.Layers.Count, Layer)
            Layer.LegendText = LegendText
            Layer.UnlockDispose()
            .Layers.ResumeEvents()
        End With
    End Sub

    ''' <summary>
    ''' Move current layer to top of list
    ''' </summary>
    Friend Sub MoveTop()
        MoveTop(Map, Layer)
    End Sub

    ''' <summary>
    ''' Move current layer up in list
    ''' </summary>
    Friend Sub MoveDown()
        With Map
            .Layers.SuspendEvents()
            Dim i As Integer = .Layers.IndexOf(Layer)
            If i > 0 Then
                Layer.LockDispose()
                Dim LegendText As String = Layer.LegendText
                .Layers.Remove(Layer)
                .Layers.Insert(i - 1, Layer)
                Layer.LegendText = LegendText
                Layer.UnlockDispose()
            End If
            .Layers.ResumeEvents()
        End With
    End Sub

    ''' <summary>
    ''' Remove current layer
    ''' </summary>
    Friend Sub Remove()
        With Map
            .Layers.Remove(Layer)
            Layer.Dispose()
        End With
    End Sub

    ''' <summary>
    ''' Control layer visibility
    ''' </summary>
    Friend Property Visible() As Boolean
        Get
            Return Layer.IsVisible
        End Get
        Set(ByVal value As Boolean)
            Layer.IsVisible = value
        End Set
    End Property

    ''' <summary>
    ''' Call this when you don't want to set Name property (which causes LoadLayers call) such as during loading
    ''' </summary>
    ''' <param name="Name">Layer name</param>
    Friend Sub SetName(ByVal Name As String)
        Layer.LegendText = Name
    End Sub

    ''' <summary>
    ''' Load layer information from ini file
    ''' </summary>
    Friend Overridable Sub Load(ByVal IniFile As clsWRDBIniFile, ByVal LayerNum As Integer)
        With IniFile
            SetName(.GetKey("Layer_" & LayerNum, clsWRDBIniFile.enumKey.GISName, "Unknown"))
            Visible = .GetKey("Layer_" & LayerNum, clsWRDBIniFile.enumKey.GISVisible, "True")
        End With
    End Sub

    ''' <summary>
    ''' Save layer information to ini file
    ''' </summary>
    Friend Overridable Sub Save(ByVal IniFile As clsWRDBIniFile, ByVal LayerNum As Integer)
        With IniFile
            .SetKey("Layer_" & LayerNum, clsWRDBIniFile.enumKey.GISShapeFile, FileName)
            .SetKey("Layer_" & LayerNum, clsWRDBIniFile.enumKey.GISShapeFileRelative, GetRelativePath(IO.Path.GetDirectoryName(.FileName), FileName)) 'path relative to ini file
            .SetKey("Layer_" & LayerNum, clsWRDBIniFile.enumKey.GISName, Name)
            .SetKey("Layer_" & LayerNum, clsWRDBIniFile.enumKey.GISVisible, Visible.ToString)
        End With
    End Sub

#Region "Public properties for property grid"

    <DisplayName("(Name)"), Category("(General)"), Description("Layer name (read-only for WRDB Stations layer).")> _
    Public Property Name() As String
        Get
            Return Layer.LegendText
        End Get
        Set(ByVal value As String)
            If Layer.LegendText = WRDBLayerName Then Exit Property
            Layer.LegendText = value
            If GISForm IsNot Nothing Then
                Dim lastIndex As Integer = GISForm.lstLayers.SelectedIndex
                GISForm.RefreshLayerList()
                GISForm.lstLayers.SelectedIndex = lastIndex
                GISForm.pgLayers.Refresh()
            End If
        End Set
    End Property

    <DisplayName("File Name"), Category("(General)"), Description("Shape file name."), [ReadOnly](True)> _
    Public ReadOnly Property FileName() As String
        Get
            Return Layer.DataSet.Filename
        End Get
    End Property

    <DisplayName("Projection"), Category("(General)"), Description("Projection string."), [ReadOnly](True)> _
    Public ReadOnly Property Projection() As String
        Get
            Return Layer.Projection.ToString
        End Get
    End Property

    Dim MyFieldListConverter As New FieldListConverter

    Private Sub AddLabels(LabelField As String,
                          LabelFont As String, LabelSize As Single, LabelColor As Color,
                          PreventCollisions As Boolean, OffsetX As Single, OffsetY As Single,
                          Orientation As ContentAlignment, Angle As Single)
        'note: font family and angle are not set probably due to bug
        Dim font = New Font(LabelFont, LabelSize)
        If LabelField <> "(None)" Then
            Map.AddLabels(Layer, "[" & LabelField & "]", font, LabelColor)
            Layer.LabelLayer.Symbolizer.OffsetX = OffsetX
            Layer.LabelLayer.Symbolizer.OffsetY = OffsetY
            Layer.LabelLayer.Symbolizer.PreventCollisions = PreventCollisions
            Layer.LabelLayer.Symbolizer.Orientation = Orientation
            Layer.LabelLayer.Symbolizer.Angle = Angle
            Layer.ShowLabels = True
        Else
            Map.ClearLabels(Layer)
        End If
        Map.Refresh()
    End Sub

    <DisplayName("Attribute Field"), Category("Labeling"), Description("Name of field to use for labels."), TypeConverter(GetType(FieldListConverter))> _
    Public Property LabelField() As String
        Set(value As String)
            AddLabels(value, LabelFont.Name, LabelFont.Size, LabelColor, PreventCollisions, OffsetX, OffsetY, Orientation, Angle)
        End Set
        Get
            Dim FieldName As String = "(None)"
            If Layer.LabelLayer IsNot Nothing AndAlso Layer.LabelLayer.Symbology IsNot Nothing AndAlso TypeOf Layer.LabelLayer.Symbology Is DotSpatial.Symbology.LabelScheme Then
                Dim ls As DotSpatial.Symbology.LabelScheme = Layer.LabelLayer.Symbology
                If ls.Categories.Count = 1 Then FieldName = ls.Categories(0).Expression.Replace("[", "").Replace("]", "")
            End If
            Return FieldName
        End Get
    End Property

    <DisplayName("Font Name & Size"), Category("Labeling"), Description("Name of font to use for labels.")> _
    Public Property LabelFont() As Font
        Set(value As Font)
            AddLabels(LabelField, value.Name, value.Size, LabelColor, PreventCollisions, OffsetX, OffsetY, Orientation, Angle)
        End Set
        Get
            Dim font As New Font("Tahoma", 8)
            If Layer.LabelLayer IsNot Nothing AndAlso Layer.LabelLayer.Symbolizer IsNot Nothing AndAlso TypeOf Layer.LabelLayer.Symbolizer Is DotSpatial.Symbology.LabelSymbolizer Then
                Dim ls As DotSpatial.Symbology.LabelSymbolizer = Layer.LabelLayer.Symbolizer
                'note: fontfamily string looks like this: [FontFamily: Name=Wingdings]
                'note: bug in library apparently prevents fonts from being set
                Dim fontname As String = ls.FontFamily.Split("=")(1).Replace("]", "")
                font = New Font(fontname, ls.FontSize)
            End If
            Return font
        End Get
    End Property

    <DisplayName("Label Color"), Category("Labeling"), Description("Color of font to use for labels.")> _
    Public Property LabelColor() As Color
        Set(value As Color)
            AddLabels(LabelField, LabelFont.Name, LabelFont.Size, value, PreventCollisions, OffsetX, OffsetY, Orientation, Angle)
        End Set
        Get
            Dim clr As Color = Color.Black
            If Layer.LabelLayer IsNot Nothing AndAlso Layer.LabelLayer.Symbolizer IsNot Nothing AndAlso TypeOf Layer.LabelLayer.Symbolizer Is DotSpatial.Symbology.LabelSymbolizer Then
                Dim ls As DotSpatial.Symbology.LabelSymbolizer = Layer.LabelLayer.Symbolizer
                clr = ls.FontColor
            End If
            Return clr
        End Get
    End Property

    <DisplayName("Label Visible?"), Category("Labeling"), Description("Controls visibility of labels.")> _
    Public Property LabelVisible As Boolean
        Set(value As Boolean)
            If value Then AddLabels(LabelField, LabelFont.Name, LabelFont.Size, LabelColor, PreventCollisions, OffsetX, OffsetY, Orientation, Angle)
            Layer.ShowLabels = value
        End Set
        Get
            Return Layer.ShowLabels
        End Get
    End Property

    <DisplayName("Prevent Collisions?"), Category("Labeling"), Description("Prevent labels from overlapping?")> _
    Public Property PreventCollisions As Boolean
        Set(value As Boolean)
            AddLabels(LabelField, LabelFont.Name, LabelFont.Size, LabelColor, value, OffsetX, OffsetY, Orientation, Angle)
        End Set
        Get
            Dim pc As Boolean = False
            If Layer.LabelLayer IsNot Nothing AndAlso Layer.LabelLayer.Symbolizer IsNot Nothing AndAlso TypeOf Layer.LabelLayer.Symbolizer Is DotSpatial.Symbology.LabelSymbolizer Then
                Dim ls As DotSpatial.Symbology.LabelSymbolizer = Layer.LabelLayer.Symbolizer
                pc = ls.PreventCollisions
            End If
            Return pc
        End Get
    End Property

    <DisplayName("Offset X"), Category("Labeling"), Description("Horizontal offset in points")> _
    Public Property OffsetX As Single
        Set(value As Single)
            AddLabels(LabelField, LabelFont.Name, LabelFont.Size, LabelColor, PreventCollisions, value, OffsetY, Orientation, Angle)
        End Set
        Get
            Dim ox As Single = 0
            If Layer.LabelLayer IsNot Nothing AndAlso Layer.LabelLayer.Symbolizer IsNot Nothing AndAlso TypeOf Layer.LabelLayer.Symbolizer Is DotSpatial.Symbology.LabelSymbolizer Then
                Dim ls As DotSpatial.Symbology.LabelSymbolizer = Layer.LabelLayer.Symbolizer
                ox = ls.OffsetX
            End If
            Return ox
        End Get
    End Property

    <DisplayName("Offset Y"), Category("Labeling"), Description("Vertical offset in points")> _
    Public Property OffsetY As Single
        Set(value As Single)
            AddLabels(LabelField, LabelFont.Name, LabelFont.Size, LabelColor, PreventCollisions, OffsetX, value, Orientation, Angle)
        End Set
        Get
            Dim oy As Single = 0
            If Layer.LabelLayer IsNot Nothing AndAlso Layer.LabelLayer.Symbolizer IsNot Nothing AndAlso TypeOf Layer.LabelLayer.Symbolizer Is DotSpatial.Symbology.LabelSymbolizer Then
                Dim ls As DotSpatial.Symbology.LabelSymbolizer = Layer.LabelLayer.Symbolizer
                oy = ls.OffsetY
            End If
            Return oy
        End Get
    End Property

    <DisplayName("Orientation"), Category("Labeling"), Description("Orientation of label")> _
    Public Property Orientation As ContentAlignment
        Set(value As ContentAlignment)
            AddLabels(LabelField, LabelFont.Name, LabelFont.Size, LabelColor, PreventCollisions, OffsetX, OffsetY, value, Angle)
        End Set
        Get
            Dim o As ContentAlignment = ContentAlignment.MiddleCenter
            If Layer.LabelLayer IsNot Nothing AndAlso Layer.LabelLayer.Symbolizer IsNot Nothing AndAlso TypeOf Layer.LabelLayer.Symbolizer Is DotSpatial.Symbology.LabelSymbolizer Then
                Dim ls As DotSpatial.Symbology.LabelSymbolizer = Layer.LabelLayer.Symbolizer
                o = ls.Orientation
            End If
            Return o
        End Get
    End Property

    <DisplayName("Angle"), Category("Labeling"), Description("Angle of label")> _
    Public Property Angle As Single
        Set(value As Single)
            AddLabels(LabelField, LabelFont.Name, LabelFont.Size, LabelColor, PreventCollisions, OffsetX, OffsetY, Orientation, value)
        End Set
        Get
            Dim a As Single = 0
            If Layer.LabelLayer IsNot Nothing AndAlso Layer.LabelLayer.Symbolizer IsNot Nothing AndAlso TypeOf Layer.LabelLayer.Symbolizer Is DotSpatial.Symbology.LabelSymbolizer Then
                Dim ls As DotSpatial.Symbology.LabelSymbolizer = Layer.LabelLayer.Symbolizer
                a = ls.Angle
            End If
            Return a
        End Get
    End Property

    'note: to avoid having to save layer structure elsewhere, use LayerKey placeholder to save data file and field names

    '<DisplayName("File Name"), Category("Data File Mapping"), Description("Data file name."), [ReadOnly](True)> _
    'Public Property DataFile() As String
    '    Get
    '        Dim Tag() As String = Map.get_LayerKey(LayerHandle).Split(";")
    '        If Tag.Length >= 1 AndAlso Not String.IsNullOrEmpty(Tag(0)) Then
    '            Return Tag(0)
    '        Else
    '            Return "Not used"
    '        End If
    '    End Get
    '    Set(ByVal value As String)
    '        Dim Tag() As String = Map.get_LayerKey(LayerHandle).Split(";")
    '        If Tag.Length >= 2 Then
    '            Map.set_LayerKey(LayerHandle, value & ";" & Tag(1))
    '        Else
    '            Map.set_LayerKey(LayerHandle, value & ";")
    '        End If
    '    End Set
    'End Property

    '<DisplayName("Field Name"), Category("Data File Mapping"), Description("Data file name."), [ReadOnly](True)> _
    'Public Property DataField() As String
    '    Get
    '        Dim Tag() As String = Map.get_LayerKey(LayerHandle).Split(";")
    '        If Tag.Length >= 2 AndAlso Not String.IsNullOrEmpty(Tag(1)) Then
    '            Return Tag(1)
    '        Else
    '            Return "Not used"
    '        End If
    '    End Get
    '    Set(ByVal value As String)
    '        Dim Tag() As String = Map.get_LayerKey(LayerHandle).Split(";")
    '        If Tag.Length >= 1 Then
    '            Map.set_LayerKey(LayerHandle, Tag(0) & ";" & value)
    '        Else
    '            Map.set_LayerKey(LayerHandle, ";" & value)
    '        End If
    '    End Set
    'End Property

#End Region

End Class




Public Class clsLayerPoint : Inherits clsLayer
    Friend Sub New(ByVal GisMap As DotSpatial.Controls.Map, Layer As DotSpatial.Symbology.FeatureLayer)
        MyBase.New(GisMap, Layer)
        'SetPointStyle(DotSpatial.Symbology.PointShape.Rectangle, 12, Color.Red, Color.White, 90)
    End Sub

    Friend Overrides Sub Load(ByVal IniFile As clsWRDBIniFile, ByVal LayerNum As Integer)
        MyBase.Load(IniFile, LayerNum)
        With IniFile
            Dim ps As DotSpatial.Symbology.PointShape = DotSpatial.Symbology.PointShape.Triangle
            [Enum].TryParse(.GetKey("Layer_" & LayerNum, clsWRDBIniFile.enumKey.GISPointType, DotSpatial.Symbology.PointShape.Rectangle.ToString), ps)
            SetPointStyle(ps,
                          .GetKey("Layer_" & LayerNum, clsWRDBIniFile.enumKey.GISPointSize, "12"),
                          Drawing.Color.FromName(.GetKey("Layer_" & LayerNum, clsWRDBIniFile.enumKey.GISPointColor, "Red")),
                          Drawing.Color.FromName(.GetKey("Layer_" & LayerNum, clsWRDBIniFile.enumKey.GISOutlineColor, "White")),
                          .GetKey("Layer_" & LayerNum, clsWRDBIniFile.enumKey.GISPointAngle, "90"))
        End With
    End Sub

    Friend Overrides Sub Save(ByVal IniFile As clsWRDBIniFile, ByVal LayerNum As Integer)
        MyBase.Save(IniFile, LayerNum)
        With IniFile
            .SetKey("Layer_" & LayerNum, clsWRDBIniFile.enumKey.GISPointColor, FillColor.Name)
            .SetKey("Layer_" & LayerNum, clsWRDBIniFile.enumKey.GISOutlineColor, OutlineColor.Name)
            .SetKey("Layer_" & LayerNum, clsWRDBIniFile.enumKey.GISPointSize, PointSize.ToString)
            .SetKey("Layer_" & LayerNum, clsWRDBIniFile.enumKey.GISPointAngle, PointAngle.ToString)
            .SetKey("Layer_" & LayerNum, clsWRDBIniFile.enumKey.GISPointType, PointShape.ToString)
        End With
    End Sub

    Friend Sub SetPointStyle(_PointShape As DotSpatial.Symbology.PointShape, _PointSize As Integer, _FillColor As Color, _OutlineColor As Color, _Angle As Double)
        'in case is thematic mapping, set size of max category (will refresh display soon)
        If Layer.Symbology.NumCategories > 0 Then
            CType(Layer.Symbology.GetCategories(Layer.Symbology.NumCategories - 1).Symbolizer, DotSpatial.Symbology.PointSymbolizer).Symbols(0).Size = New DotSpatial.Symbology.Size2D(_PointSize, _PointSize)
        End If
        Dim sym As New DotSpatial.Symbology.PointSymbolizer
        Dim ss As New DotSpatial.Symbology.SimpleSymbol(_FillColor, _PointShape, _PointSize)
        ss.OutlineColor = _OutlineColor
        ss.OutlineWidth = 1
        ss.UseOutline = True
        ss.Angle = _Angle
        sym.Symbols.Clear()
        sym.Symbols.Add(ss)
        Layer.Symbolizer = sym
        'If Map.Enabled Then Map.Refresh()
    End Sub

    <DisplayName("Fill"), Category("Point Settings"), Description("Point color.")> _
    Public Property FillColor() As Drawing.Color
        Get
            If Layer.Symbolizer IsNot Nothing Then
                Return GetKnownColor(CType(Layer.Symbolizer, DotSpatial.Symbology.PointSymbolizer).GetFillColor)
            Else
                Return Color.White
            End If
        End Get
        Set(ByVal value As Drawing.Color)
            SetPointStyle(PointShape, PointSize, value, OutlineColor, PointAngle)
        End Set
    End Property

    <DisplayName("Outline"), Category("Point Settings"), Description("Outline color.")> _
    Public Property OutlineColor() As Drawing.Color
        Get
            If Layer.Symbolizer IsNot Nothing Then
                Return GetKnownColor(CType(CType(Layer.Symbolizer, DotSpatial.Symbology.PointSymbolizer).Symbols(0), DotSpatial.Symbology.SimpleSymbol).OutlineColor)
            Else
                Return Color.White
            End If
        End Get
        Set(ByVal value As Drawing.Color)
            SetPointStyle(PointShape, PointSize, FillColor, value, PointAngle)
        End Set
    End Property

    <DisplayName("Size"), Category("Point Settings"), Description("Point size (or average point size for thematic mapping).")> _
    Public Property PointSize() As Double
        Get
            If Layer.Symbolizer IsNot Nothing Then
                If Layer.Symbology.NumCategories > 0 Then
                    Return CType(Layer.Symbology.GetCategories(Layer.Symbology.NumCategories - 1).Symbolizer, DotSpatial.Symbology.PointSymbolizer).Symbols(0).Size.Width
                Else
                    Return CType(Layer.Symbolizer, DotSpatial.Symbology.PointSymbolizer).GetSize.Width
                End If
            Else
                Return 1
            End If
        End Get
        Set(ByVal value As Double)
            SetPointStyle(PointShape, value, FillColor, OutlineColor, PointAngle)
        End Set
    End Property

    <DisplayName("Angle"), Category("Point Settings"), Description("Point angle.")> _
    Public Property PointAngle() As Double
        Get
            If Layer.Symbolizer IsNot Nothing Then
                Return CType(CType(Layer.Symbolizer, DotSpatial.Symbology.PointSymbolizer).Symbols(0), DotSpatial.Symbology.SimpleSymbol).Angle
            Else
                Return 1
            End If
        End Get
        Set(ByVal value As Double)
            SetPointStyle(PointShape, PointSize, FillColor, OutlineColor, value)
        End Set
    End Property

    <DisplayName("Type"), Category("Point Settings"), Description("Point type.")> _
    Public Property PointShape() As DotSpatial.Symbology.PointShape
        Get
            If Layer.Symbolizer IsNot Nothing Then
                Return CType(CType(Layer.Symbolizer, DotSpatial.Symbology.PointSymbolizer).Symbols(0), DotSpatial.Symbology.SimpleSymbol).PointShape
            Else
                Return DotSpatial.Symbology.PointShape.Rectangle
            End If
        End Get
        Set(ByVal value As DotSpatial.Symbology.PointShape)
            SetPointStyle(value, PointSize, FillColor, OutlineColor, PointAngle)
        End Set
    End Property

End Class

Public Class clsLayerPolyline : Inherits clsLayer
    Friend Sub New(ByVal GisMap As DotSpatial.Controls.Map, Layer As DotSpatial.Symbology.FeatureLayer)
        MyBase.New(GisMap, Layer)
    End Sub

    Friend Overrides Sub Load(ByVal IniFile As clsWRDBIniFile, ByVal LayerNum As Integer)
        MyBase.Load(IniFile, LayerNum)
        With IniFile
            LineColor = Drawing.Color.FromName(.GetKey("Layer_" & LayerNum, clsWRDBIniFile.enumKey.GISLineColor, "Black"))
            LineWidth = .GetKey("Layer_" & LayerNum, clsWRDBIniFile.enumKey.GISLineWidth, "0")
            LinePattern = Drawing2D.DashStyle.Solid
            [Enum].TryParse(.GetKey("Layer_" & LayerNum, clsWRDBIniFile.enumKey.GISLineStipple, Drawing2D.DashStyle.Solid.ToString), LinePattern)
        End With
    End Sub

    Friend Overrides Sub Save(ByVal IniFile As clsWRDBIniFile, ByVal LayerNum As Integer)
        MyBase.Save(IniFile, LayerNum)
        With IniFile
            .SetKey("Layer_" & LayerNum, clsWRDBIniFile.enumKey.GISLineColor, LineColor.Name)
            .SetKey("Layer_" & LayerNum, clsWRDBIniFile.enumKey.GISLineWidth, LineWidth.ToString)
            .SetKey("Layer_" & LayerNum, clsWRDBIniFile.enumKey.GISLineStipple, LinePattern.ToString)
        End With
    End Sub

    <DisplayName("Color"), Category("Polyline Settings"), Description("Line color.")> _
    Public Property LineColor() As Drawing.Color
        Get
            If Layer.Symbolizer IsNot Nothing Then
                If TypeOf Me Is clsLayerPolygon Then
                    If Layer.Symbology IsNot Nothing AndAlso TypeOf Layer.Symbology Is DotSpatial.Symbology.PolygonScheme AndAlso CType(Layer.Symbology, DotSpatial.Symbology.PolygonScheme).NumCategories >= 2 Then
                        Return Color.LightGray
                    Else
                        With CType(Layer.Symbolizer, DotSpatial.Symbology.PolygonSymbolizer)
                            If .OutlineSymbolizer IsNot Nothing AndAlso .OutlineSymbolizer.Strokes.Count = 1 Then Return GetKnownColor(CType(.OutlineSymbolizer.Strokes(0), DotSpatial.Symbology.SimpleStroke).Color)
                        End With
                    End If
                Else
                    With CType(Layer.Symbolizer, DotSpatial.Symbology.LineSymbolizer)
                        If .Strokes.Count = 1 Then Return GetKnownColor(CType(.Strokes(0), DotSpatial.Symbology.SimpleStroke).Color)
                    End With
                End If
                End If
            Return Color.White
        End Get
        Set(ByVal value As Drawing.Color)
            Dim StrokeList As Generic.IList(Of DotSpatial.Symbology.IStroke) = Nothing
            If Layer.Symbolizer IsNot Nothing Then
                If TypeOf Me Is clsLayerPolygon Then
                    With CType(Layer.Symbolizer, DotSpatial.Symbology.PolygonSymbolizer)

                        If .OutlineSymbolizer IsNot Nothing Then StrokeList = .OutlineSymbolizer.Strokes
                    End With
                Else
                    StrokeList = CType(Layer.Symbolizer, DotSpatial.Symbology.LineSymbolizer).Strokes
                End If
            End If
            If StrokeList Is Nothing Then Exit Property
            With StrokeList
                Dim stroke As New DotSpatial.Symbology.SimpleStroke(LineWidth, value)
                stroke.DashStyle = LinePattern
                .Clear()
                .Add(stroke)
            End With
            If Map.Enabled Then Map.Refresh()
        End Set
    End Property

    <DisplayName("Width"), Category("Polyline Settings"), Description("Line width.")> _
    Public Property LineWidth() As Single
        Get
            If Layer.Symbolizer IsNot Nothing Then
                If TypeOf Me Is clsLayerPolygon Then
                    With CType(Layer.Symbolizer, DotSpatial.Symbology.PolygonSymbolizer)
                        If .OutlineSymbolizer IsNot Nothing AndAlso .OutlineSymbolizer.Strokes.Count = 1 Then Return CType(.OutlineSymbolizer.Strokes(0), DotSpatial.Symbology.SimpleStroke).Width
                    End With
                Else
                    With CType(Layer.Symbolizer, DotSpatial.Symbology.LineSymbolizer)
                        If .Strokes.Count = 1 Then Return CType(.Strokes(0), DotSpatial.Symbology.SimpleStroke).Width
                    End With
                End If
            End If
            Return 1
        End Get
        Set(ByVal value As Single)
            Dim StrokeList As Generic.IList(Of DotSpatial.Symbology.IStroke) = Nothing
            If Layer.Symbolizer IsNot Nothing Then
                If TypeOf Me Is clsLayerPolygon Then
                    With CType(Layer.Symbolizer, DotSpatial.Symbology.PolygonSymbolizer)
                        If .OutlineSymbolizer IsNot Nothing Then StrokeList = .OutlineSymbolizer.Strokes
                    End With
                Else
                    StrokeList = CType(Layer.Symbolizer, DotSpatial.Symbology.LineSymbolizer).Strokes
                End If
            End If
            If StrokeList Is Nothing Then Exit Property
            With StrokeList
                Dim stroke As New DotSpatial.Symbology.SimpleStroke(value, LineColor)
                stroke.DashStyle = LinePattern
                .Clear()
                .Add(stroke)
            End With
            If Map.Enabled Then Map.Refresh()
        End Set
    End Property

    <DisplayName("Pattern"), Category("Polyline Settings"), Description("Pattern used for line or border.")> _
    Public Property LinePattern() As Drawing2D.DashStyle
        Get
            If Layer.Symbolizer IsNot Nothing Then
                If TypeOf Me Is clsLayerPolygon Then
                    With CType(Layer.Symbolizer, DotSpatial.Symbology.PolygonSymbolizer)
                        If .OutlineSymbolizer IsNot Nothing AndAlso .OutlineSymbolizer.Strokes.Count = 1 Then Return CType(.OutlineSymbolizer.Strokes(0), DotSpatial.Symbology.SimpleStroke).DashStyle
                    End With
                Else
                    With CType(Layer.Symbolizer, DotSpatial.Symbology.LineSymbolizer)
                        If .Strokes.Count = 1 Then Return CType(.Strokes(0), DotSpatial.Symbology.SimpleStroke).DashStyle
                    End With
                End If
            End If
            Return Drawing2D.DashStyle.Solid
        End Get
        Set(ByVal value As Drawing2D.DashStyle)
            Dim StrokeList As Generic.IList(Of DotSpatial.Symbology.IStroke) = Nothing
            If Layer.Symbolizer IsNot Nothing Then
                If TypeOf Me Is clsLayerPolygon Then
                    With CType(Layer.Symbolizer, DotSpatial.Symbology.PolygonSymbolizer)
                        If .OutlineSymbolizer IsNot Nothing Then StrokeList = .OutlineSymbolizer.Strokes
                    End With
                Else
                    StrokeList = CType(Layer.Symbolizer, DotSpatial.Symbology.LineSymbolizer).Strokes
                End If
            End If
            If StrokeList Is Nothing Then Exit Property
            With StrokeList
                Dim stroke As New DotSpatial.Symbology.SimpleStroke(LineWidth, LineColor)
                stroke.DashStyle = value
                .Clear()
                .Add(stroke)
            End With
            If Map.Enabled Then Map.Refresh()
        End Set
    End Property

End Class



Public Class clsLayerPolygon : Inherits clsLayerPolyline
    Private PreviousColor As Color = Color.Red

    Friend Sub New(ByVal GisMap As DotSpatial.Controls.Map, Layer As DotSpatial.Symbology.FeatureLayer)
        MyBase.New(GisMap, Layer)
    End Sub

    Friend Overrides Sub Load(ByVal IniFile As clsWRDBIniFile, ByVal LayerNum As Integer)
        MyBase.Load(IniFile, LayerNum)
        With IniFile
            FillColor = Drawing.Color.FromName(.GetKey("Layer_" & LayerNum, clsWRDBIniFile.enumKey.GISFillColor, "White"))
            Filled = .GetKey("Layer_" & LayerNum, clsWRDBIniFile.enumKey.GISFilled, "True")
            'FillStipple = [Enum].Parse(GetType(Drawing2D.DashStyle), .GetKey("Layer_" & LayerNum, clsWRDBIniFile.enumKey.GISFillStipple, Drawing2D.DashStyle.Solid.ToString))
            FillOpacity = .GetKey("Layer_" & LayerNum, clsWRDBIniFile.enumKey.GISFillTransparency, "1")
        End With
    End Sub

    Friend Overrides Sub Save(ByVal IniFile As clsWRDBIniFile, ByVal LayerNum As Integer)
        MyBase.Save(IniFile, LayerNum)
        With IniFile
            .SetKey("Layer_" & LayerNum, clsWRDBIniFile.enumKey.GISFilled, Filled.ToString)
            .SetKey("Layer_" & LayerNum, clsWRDBIniFile.enumKey.GISFillColor, FillColor.Name)
            '.SetKey("Layer_" & LayerNum, clsWRDBIniFile.enumKey.GISFillStipple, FillStipple.ToString)
            .SetKey("Layer_" & LayerNum, clsWRDBIniFile.enumKey.GISFillTransparency, FillOpacity.ToString)
        End With
    End Sub

    <DisplayName("Filled?"), Category("Polygon Fill Settings"), Description("Shapes are filled?")> _
    Public Property Filled() As Boolean
        Get
            If Layer.Symbolizer IsNot Nothing Then
                Return CType(Layer.Symbolizer, DotSpatial.Symbology.PolygonSymbolizer).GetFillColor() <> Color.Transparent
            Else
                Return False
            End If
        End Get
        Set(ByVal value As Boolean)
            If value Then
                FillColor = PreviousColor
            Else
                PreviousColor = FillColor
                FillColor = Color.Transparent
            End If
        End Set
    End Property

    <DisplayName("Color"), Category("Polygon Fill Settings"), Description("Fill color.")> _
    Public Property FillColor() As Drawing.Color
        Get
            If Layer.Symbology IsNot Nothing AndAlso TypeOf Layer.Symbology Is DotSpatial.Symbology.PolygonScheme AndAlso CType(Layer.Symbology, DotSpatial.Symbology.PolygonScheme).NumCategories >= 2 Then
                Return GetKnownColor(CType(Layer.Symbology, DotSpatial.Symbology.PolygonScheme).Categories(1).GetColor)
            ElseIf Layer.Symbolizer IsNot Nothing Then
                Return GetKnownColor(CType(Layer.Symbolizer, DotSpatial.Symbology.PolygonSymbolizer).GetFillColor)
            Else
                Return Color.Red
            End If
        End Get
        Set(ByVal value As Drawing.Color)
            If Layer.Symbolizer IsNot Nothing Then
                CType(Layer.Symbolizer, DotSpatial.Symbology.PolygonSymbolizer).SetFillColor(value)
                If Map.Enabled Then Map.Refresh()
            End If
            PreviousColor = value
        End Set
    End Property

    '<DisplayName("Pattern"), Category("Polygon Fill Settings"), Description("Pattern used to fill shapes")> _
    'Public Property FillStipple() As Drawing2D.DashStyle
    '    Get
    '        Return Map.get_ShapeLayerFillStipple(LayerHandle)
    '    End Get
    '    Set(ByVal value As Drawing2D.DashStyle)
    '        Map.set_ShapeLayerFillStipple(LayerHandle, value)
    '    End Set
    'End Property

    <DisplayName("Opacity"), Category("Polygon Fill Settings"), Description("Opacity used to fill shapes (0.0=transparent, 1.0=opaque).")> _
    Public Property FillOpacity() As Single
        Get
            If Layer.Symbolizer IsNot Nothing AndAlso TypeOf CType(Layer.Symbolizer, DotSpatial.Symbology.PolygonSymbolizer).Patterns(0) Is DotSpatial.Symbology.SimplePattern Then
                Return Math.Round(CType(CType(Layer.Symbolizer, DotSpatial.Symbology.PolygonSymbolizer).Patterns(0), DotSpatial.Symbology.SimplePattern).Opacity, 2)
            Else
                Return 1.0
            End If
        End Get
        Set(ByVal value As Single)
            If Layer.Symbolizer IsNot Nothing Then
                With CType(Layer.Symbolizer, DotSpatial.Symbology.PolygonSymbolizer)
                    Dim sp As New DotSpatial.Symbology.SimplePattern(FillColor)
                    If .Patterns.Count = 1 Then sp = .Patterns(0)
                    sp.Opacity = value
                    .Patterns.Clear()
                    .Patterns.Add(sp)
                End With
            End If
        End Set
    End Property

End Class

'Public Class ImageConverter
'    Inherits System.Windows.Forms.AxHost
'    Public Sub New()
'        MyBase.New("00000000-9999-0000-9999-000000000001")
'    End Sub
'    Public Function ImageToPicture(ByRef Data As Image) As Object
'        Return AxHost.GetIPictureDispFromPicture(Data)
'    End Function
'End Class

Public Module modGIS
    <System.Runtime.InteropServices.DllImportAttribute("gdi32.dll")> _
    Friend Function BitBlt(ByVal hdcDest As IntPtr, ByVal nXDest As Integer, ByVal nYDest As Integer, ByVal nWidth As Integer, ByVal nHeight As Integer, ByVal hdcSrc As IntPtr, ByVal nXSrc As Integer, ByVal nYSrc As Integer, ByVal dwRop As System.Int32) As Boolean
    End Function
    Public DefaultLayerColor(1399) As Drawing.Color
    Friend GISForm As frmGIS
    Public ProjectProjection As DotSpatial.Projections.ProjectionInfo
    'Public Const LatLongProjection As String = "+proj=latlong +ellps=WGS84 +datum=WGS84" '"+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs"
    Friend CurrentLayer As DotSpatial.Symbology.FeatureLayer  'shared so typeconverter can display list of fields
    Friend WRDBProject As WRDB.Project.clsProject
    Friend ActiveTableName As String
    Friend TempData As String
    Friend WRDBStationTypes As New Generic.List(Of String) 'prior list of station types (so won't have to requery data maybe)
    Friend Const REGAPP As String = "WRDBGIS"

    ''' <summary>Name of WRDB Station layer</summary>
    Friend WRDBLayerName As String = "WRDB Stations"

    ''' <summary>Filename of WRDB Station layer</summary>
    Friend WRDBFileName As String = "WRDBSta"

    ''' <summary>List of stations to display (Stations mode only); string must be tab separated and contain StationID, Name, Type, Lat, Long</summary>
    Friend StationList As New Generic.List(Of String)

    ''' <summary>
    ''' Name of directory for temporary files (does NOT end with backslash)
    ''' </summary>
    Friend TempPath As String = String.Format("{0}\WRDB", My.Computer.FileSystem.SpecialDirectories.Temp)

    'Public Function MapColor(ByVal SystemColor As Color) As UInt32
    '    Return System.Convert.ToUInt32(RGB(SystemColor.R, SystemColor.G, SystemColor.B))
    'End Function

    ''' <summary>
    ''' Reproject a shapefile to a new projection
    ''' </summary>
    ''' <param name="Map">Map object</param>
    ''' <param name="ShapeFileName">Old shapefile name; if ReplaceOrig is false, this returns the newly reprojected shape file name</param>
    ''' <param name="NewProj">New projection string</param>
    ''' <param name="ReplaceOrig">If true, will replace the original shape file with the new one</param>
    Public Function Reproject(ByVal Map As DotSpatial.Controls.Map, ByRef ShapeFileName As String, ByVal NewProj As DotSpatial.Projections.ProjectionInfo, ByVal ReplaceOrig As Boolean) As Boolean
        Dim fs As New DotSpatial.Data.FeatureSet
        fs.Open(ShapeFileName)
        With fs
            If .DataTable.Rows.Count > 0 AndAlso Not .Projection.Equals(NewProj) AndAlso NewProj.IsValid Then
                Dim NewFilename As String = .Filename.Replace(".shp", "-reproject.shp")
                If My.Computer.FileSystem.FileExists(NewFilename) Then DeleteShapeFiles(NewFilename)
                Dim OldProj As DotSpatial.Projections.ProjectionInfo = .Projection
                .Close()
                'if going to reproject and current projection is unknown, assume is lat/long
                If Not fs.Projection.IsValid Then fs.Projection = DotSpatial.Projections.KnownCoordinateSystems.Geographic.World.WGS1984
                fs.Reproject(NewProj)
                If ReplaceOrig Then
                    fs.Save()
                Else
                    fs.SaveAs(NewFilename, True)
                End If
            End If
        End With
        Return True
    End Function

#Region "Map Functions..."

    Friend Function GetFeatureSet(ByVal GISMap As DotSpatial.Controls.Map, ByVal LayerName As String) As DotSpatial.Data.FeatureSet
        For Each lyr As DotSpatial.Symbology.FeatureLayer In GISMap.Layers
            If lyr.LegendText = LayerName Then
                Return lyr.DataSet
            End If
        Next
        Return Nothing
    End Function

    Friend Function GetFeatureSet(ByVal GISMap As DotSpatial.Controls.Map, ByVal LayerPosition As Integer) As DotSpatial.Data.FeatureSet
        Return GISMap.Layers(LayerPosition).DataSet
    End Function

    Friend Function GetLayerIndex(ByVal GISMap As DotSpatial.Controls.Map, ByVal LayerName As String) As Integer
        For Each lyr As DotSpatial.Symbology.FeatureLayer In GISMap.Layers
            If lyr.LegendText = LayerName Then
                Return GISMap.Layers.IndexOf(lyr)
            End If
        Next
        Return -1
    End Function

    Friend Function GetLayer(ByVal GISMap As DotSpatial.Controls.Map, ByVal LayerName As String) As DotSpatial.Symbology.FeatureLayer
        For Each lyr As DotSpatial.Symbology.FeatureLayer In GISMap.Layers
            If lyr.LegendText = LayerName Then Return lyr
        Next
        Return Nothing
    End Function

    Friend Function GetFieldIndex(ByVal dt As DataTable, ByVal FieldName As String)
        For Each c As DataColumn In dt.Columns
            If c.ColumnName.ToUpper.Equals(FieldName.ToUpper) Then Return dt.Columns.IndexOf(c)
        Next
        Return -1
    End Function

    ''' <summary>
    ''' Deletes all files that have same filename as shapefile (e.g., .prj, .idx, .shx, etc.)
    ''' </summary>
    ''' <param name="ShapeFileName">Name of shape file</param>
    Friend Sub DeleteShapeFiles(ShapeFileName As String)
        For i As Int16 = 1 To 5
            Dim ext As String = Choose(i, "shp", "shx", "dbf", "prj", "mwsr")
            Dim fn As String = IO.Path.ChangeExtension(ShapeFileName, ext)
            If My.Computer.FileSystem.FileExists(fn) Then My.Computer.FileSystem.DeleteFile(fn)
        Next
    End Sub

#End Region

    ''' <summary>
    ''' Verify whether file exists in one of two locations; if not, offer to browse to reselect
    ''' </summary>
    ''' <param name="FullFilePath">Full absolute file path to first location</param>
    ''' <param name="BaseDirectory">Full absolute directory path to second location</param>
    ''' <param name="RelativeFilePath">Relative file path to second location</param>
    ''' <returns>Full path of file if found or selected; empty string if cancel at file dialog</returns>
    ''' <remarks>Will first look for file containing full path, then for file at relative location, then in base directory, then will browse</remarks>
    Friend Function FindFile(ByVal FullFilePath As String, Optional ByVal BaseDirectory As String = "", Optional ByVal RelativeFilePath As String = "") As String

        'Static UseSelection As Boolean = False
        Static UseSelection As Boolean = True 'for now, never show dialog and always select in order 

        Dim OrigPath As String = "", RelPath As String = "", LocalPath As String = ""
        Dim NumFound As Integer = 0

        If FullFilePath <> "" And My.Computer.FileSystem.FileExists(FullFilePath) Then OrigPath = FullFilePath : NumFound += 1
        Dim FullFilePath2 As String = ""
        If BaseDirectory <> "" AndAlso My.Computer.FileSystem.DirectoryExists(BaseDirectory) Then
            My.Computer.FileSystem.CurrentDirectory = BaseDirectory
            If RelativeFilePath <> "" Then
                FullFilePath2 = IO.Path.GetFullPath(RelativeFilePath)
                If My.Computer.FileSystem.FileExists(FullFilePath2) Then RelPath = FullFilePath2 : NumFound += 1
            End If
            FullFilePath2 = IO.Path.GetFullPath(BaseDirectory & "\" & IO.Path.GetFileName(FullFilePath))
            If My.Computer.FileSystem.FileExists(FullFilePath2) Then LocalPath = FullFilePath2 : NumFound += 1
            If RelPath = LocalPath Then RelPath = "" : NumFound -= 1
            If OrigPath = LocalPath Then OrigPath = "" : NumFound -= 1
        End If

        Dim path() As String = {OrigPath, RelPath, LocalPath}

        If NumFound = 1 Or UseSelection Then
            If OrigPath <> "" Then Return OrigPath
            If RelPath <> "" Then Return RelPath
            Return LocalPath
        Else
            If NumFound > 1 Then
                With New frmFindFile
                    Dim opt() As RadioButton = {.optOrig, .optRel, .optLocal}
                    Dim txt() As TextBox = {.txtOrig, .txtRel, .txtLocal}
                    For i As Integer = 0 To 2
                        If path(i) <> "" Then
                            opt(i).Enabled = True
                            txt(i).Text = path(i)
                            .ToolTip1.SetToolTip(txt(i), path(i))
                            opt(i).Checked = True 'set last one
                        Else
                            opt(i).Enabled = False
                        End If
                    Next
                    If .ShowDialog = DialogResult.OK Then
                        For i As Integer = 0 To 2
                            If opt(i).Checked Then
                                UseSelection = .chkDontDisplay.Checked
                                Return path(i)
                            End If
                        Next
                        'will browse below
                    Else
                        Return ""
                    End If
                    .Dispose()
                End With
            End If
        End If

        'no files found on any path or user selected browse option

        With New OpenFileDialog
            .FileName = IO.Path.GetFileName(FullFilePath)
            .InitialDirectory = IO.Path.GetDirectoryName(FullFilePath)
            Dim ext As String = IO.Path.GetExtension(FullFilePath)
            .DefaultExt = ext
            .Filter = String.Format("{0} files|*{0}|All files|*.*", ext)
            .FilterIndex = 0
            .Multiselect = False
            .Title = "File not found; please browse to select"
            If .ShowDialog = DialogResult.OK Then
                If .FileName <> "" And My.Computer.FileSystem.FileExists(.FileName) Then Return .FileName
            End If
            Return ""
        End With

    End Function

    Friend Sub ShowHelp()
        Dim HelpFile As String = My.Application.Info.DirectoryPath & "\WRDB.chm"
        If Debugger.IsAttached Then HelpFile = "C:\Projects\WRDB 5.0\Help\HTMLHelp\WRDB.chm"
        If My.Computer.FileSystem.FileExists(HelpFile) Then
            Help.ShowHelp(Form.ActiveForm, HelpFile)
        Else
            WarningMsg("Unable to find help file: {0}", HelpFile.Replace("\", "/"))
        End If
    End Sub

    Friend Sub ShowHelp(ByVal OwnerForm As Form, ByVal TopicID As String)
        'note: to assign map IDs to help topics, in RoboHelp HTML select Project tab, then Context Sensitive Help, then Aliases, then Map File WRDB.h
        Dim HelpFile As String = My.Application.Info.DirectoryPath & "\WRDB.chm"
        If Debugger.IsAttached Then HelpFile = "C:\Projects\WRDB 5.0\HELP\HTMLHelp\WRDB.chm"
        If My.Computer.FileSystem.FileExists(HelpFile) Then
            Help.ShowHelp(OwnerForm, HelpFile, HelpNavigator.TopicId, TopicID)
        Else
            WarningMsg(StringFormat("Unable to find help file named '{0}'; please restore this file or reinstall application.", HelpFile.Replace("\", "\\")))
        End If
    End Sub

    Private ColorCache As New Dictionary(Of Integer, Color)

    Public Function GetKnownColor(Color As Color) As Color
        Dim iArgb As Integer = Color.ToArgb
        If ColorCache.ContainsKey(iArgb) Then Return ColorCache(iArgb)

        Dim nearest_Color As Color = Drawing.Color.Empty
        Dim distance As Double = 500.0

        For Each kcv As KnownColor In [Enum].GetValues(GetType(KnownColor))
            Dim kc As Color = Color.FromKnownColor(kcv)
            Dim dRed As Double = Convert.ToDouble(Color.R)
            Dim dGrn As Double = Convert.ToDouble(Color.G)
            Dim dBlu As Double = Convert.ToDouble(Color.B)
            Dim distRed As Double = Math.Pow(kc.R - dRed, 2.0)
            Dim distGrn As Double = Math.Pow(kc.G - dGrn, 2.0)
            Dim distBlu As Double = Math.Pow(kc.B - dBlu, 2.0)
            Dim dist As Double = Math.Sqrt(distBlu + distGrn + distRed)
            If Not kc.IsSystemColor Then
                If dist = 0.0 Then
                    nearest_Color = kc
                    Exit For
                ElseIf dist < distance Then
                    distance = dist
                    nearest_Color = kc
                End If
            End If
        Next
        ColorCache.Add(iArgb, nearest_Color)
        Return nearest_Color
    End Function

    Public Sub Main()
        With New frmGIS
            .ShowDialog()
            .Dispose()
        End With
    End Sub
End Module


''' <summary>
''' This was tested for display of available field names in selected layer, but decided to go another direction
''' </summary>
''' <remarks></remarks>
Public Class FieldListConverter
    Inherits TypeConverter

    Public Overrides Function GetStandardValuesSupported(ByVal context As System.ComponentModel.ITypeDescriptorContext) As Boolean
        Return True
    End Function

    Public Overrides Function GetStandardValuesExclusive(ByVal context As System.ComponentModel.ITypeDescriptorContext) As Boolean
        Return True
    End Function

    Public Overrides Function GetStandardValues(ByVal context As System.ComponentModel.ITypeDescriptorContext) As System.ComponentModel.TypeConverter.StandardValuesCollection
        Dim lstFields As New Generic.List(Of String)

        lstFields.Add("(None)")
        For i As Integer = 0 To CurrentLayer.DataSet.DataTable.Columns.Count - 1
            lstFields.Add(CurrentLayer.DataSet.DataTable.Columns(i).ColumnName)
        Next
        lstFields.Sort()
        Return New StandardValuesCollection(lstFields)
    End Function
End Class
